﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;

namespace WebApplication1.App_Start
{
    /// <summary>
    /// 自定义ControllerSelector(控制器选择)
    /// </summary>
    public class VersionHttpControllerSelector : DefaultHttpControllerSelector
    {
        private readonly HttpConfiguration _configuration;
        private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _lazyMappingDictionary;
        private const string DefaultVersion = "v1"; //默认版本号，因为之前的api我们没有版本号的概念
        private const string DefaultNamespaces = "WebApplication1.Controllers";
        private const string RouteVersionKey = "version"; //路由规则中Version的字符串
        private const string DictKeyFormat = "{0}.{1}";
        private const string ControllerName = "Client";//Route自定义Controller
        public VersionHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
        {
            _configuration = configuration;
            _lazyMappingDictionary = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDict);

        }
        private Dictionary<string, HttpControllerDescriptor> InitializeControllerDict()
        {

            var result = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
            var assemblies = _configuration.Services.GetAssembliesResolver();
            var controllerResolver = _configuration.Services.GetHttpControllerTypeResolver();
            var controllerTypes = controllerResolver.GetControllerTypes(assemblies);

            foreach (var t in controllerTypes)
            {
                if (t.Namespace.Contains(DefaultNamespaces)) //符合NameSpace规则
                {
                    var segments = t.Namespace.Split(Type.Delimiter);
                    var version = t.Namespace.Equals(DefaultNamespaces, StringComparison.OrdinalIgnoreCase) ?
                        DefaultVersion : segments[segments.Length - 1];
                    var controllerName = ControllerName;//t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);(不使用路由规则时,请换成默认控制器)
                    var key = string.Format(DictKeyFormat, version, controllerName);
                    if (!result.ContainsKey(key))
                    {
                        result.Add(key, new HttpControllerDescriptor(_configuration, t.Name, t));
                    }
                }
            }
            return result;
        }
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            IHttpRouteData routeData = request.GetRouteData();
            if (routeData == null)
                throw new HttpResponseException(HttpStatusCode.NotFound);

            var controllerName = ControllerName;//GetControllerName(request);(不使用路由规则时,请换成routeData获取)
            if (String.IsNullOrEmpty(controllerName))
                throw new HttpResponseException(HttpStatusCode.NotFound);

            var version = DefaultVersion;
            if (IsVersionRoute(routeData, out version))
            {
                var key = String.Format(DictKeyFormat, version, controllerName);
                if (_lazyMappingDictionary.Value.ContainsKey(key))
                {
                    return _lazyMappingDictionary.Value[key];
                }

                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            return base.SelectController(request);
        }
        private bool IsVersionRoute(IHttpRouteData routeData, out string version)
        {
            version = String.Empty;
            var prevRouteTemplate = "api/{controller}/{action}/{id}";
            object outVersion;
            if (routeData.Values.TryGetValue(RouteVersionKey, out outVersion))   //先找符合新规则的路由版本
            {
                version = outVersion.ToString();
                return true;
            }
            if (routeData.Route.RouteTemplate.Contains(prevRouteTemplate))  //不符合再比对是否符合原先的api路由
            {
                version = DefaultVersion;
                return true;
            }
            return false;
        }

    }
}